import numpy as np

def cosine_similarity(a, b):
    """Compute the cosine similarity between vectors a and b."""
    return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))


def gradient_cosine_similarity(a, b):
    """Compute the gradient of cosine similarity with respect to b."""
    b_norm = np.linalg.norm(b)
    a_dot_b = np.dot(a, b)
    
    grad = -(a / b_norm - (a_dot_b * b) / (b_norm**3))
    return grad


def optimize_b(a, b, learning_rate=0.01, iterations=100):
    """Optimize b to maximize cosine similarity with a."""
    for i in range(iterations):
        grad = gradient_cosine_similarity(a, b)
        b -= learning_rate * grad

        # Normalize b to avoid it becoming too large or too small
        b = b / np.linalg.norm(b)

        if i % 10 == 0:  # Print progress every 10 iterations
            similarity = cosine_similarity(a, b)
            print(f"Iteration {i}: Cosine Similarity = {similarity:.4f}")

    return b

# Define the input vectors
a = np.array([1, 2, 3, 4, 5, 6], dtype=np.float64)
b = np.array([2, 3, 4, 1, 3, 4], dtype=np.float64)

# Initial cosine similarity
initial_similarity = cosine_similarity(a, b)
print(f"Initial Cosine Similarity: {initial_similarity:.4f}")

# Optimize b
optimized_b = optimize_b(a, b, learning_rate=0.1, iterations=100)

# Final cosine similarity
final_similarity = cosine_similarity(a, optimized_b)
print(f"Final Cosine Similarity: {final_similarity:.4f}")

# Print optimized b
print("Optimized b:", optimized_b)
